﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ReBasic.Helper
{
    public class ConcurrenceHelper
    {
        public event MainFrame.InfoHandler StateEvent;
        public event MainFrame.ProcessHandler ProcessEvent;

        byte[] MskHeToIdx;
        int[] co_occur_r_t;
        int[] co_occur_h_r;
        Dictionary<int, string> IdxToString;    //Use index to find string that can be a candidate of target entity

        public Dictionary<int, int>[] TotalMatrix;
        public Dictionary<int, int>[] SubMatrix;
        public Dictionary<int, StringBuilder>[] SubMatrixOccPos;    //store co-occur pos for each <h, t> pair
        // Concurrence Matrix
        // First indexer is the idx of he in host_entity_set
        // Second index is the idx of te in SubConDict

        /// <summary>
        /// Take all co-occurrences we need in one sentence.
        /// </summary>
        /// <param name="Sent">The sentence</param>
        /// <param name="SubConDict">Sub-concepts Dictionary</param>
        void InsertTermToMatrix(string[] terms, Dictionary<string, int> SubConDict)
        {
            int i, j, len, hidx, tidx;
            len = terms.Length;
            if (len < 2) return;
            for (i = 0; i < len; i++)
            {
                hidx = MskHeToIdx[SubConDict[terms[i]]];
                if (hidx == 255) continue;
                for (j = 0; j < len; j++)
                {
                    if (j == i) continue;
                    tidx = SubConDict[terms[j]];
                    if (TotalMatrix[hidx].ContainsKey(tidx))
                        TotalMatrix[hidx][tidx]++;
                    else
                        TotalMatrix[hidx].Add(tidx, 1);
                }
            }
        }

        /// <summary>
        /// Take all co-occurrences we need in one sentence, put it into SubMatrix.
        /// </summary>
        /// <param name="terms">The sentence</param>
        /// <param name="termIdx">Idx for the sentence</param>
        /// <param name="SubConDict">Sub-concepts Dictionary</param>
        void InsertTermToSubMatrix(string[] terms, int termIdx, Dictionary<string, int> SubConDict)
        {
            StringBuilder sb;
            int i, j, len, hidx, tidx;
            len = terms.Length;
            if (len < 2) return;
            for (i = 0; i < len; i++)
            {
                co_occur_r_t[SubConDict[terms[i]]]++;
                hidx = MskHeToIdx[SubConDict[terms[i]]];
                if (hidx == 255) continue;
                co_occur_h_r[hidx]++;
                for (j = 0; j < len; j++)
                {
                    if (i == j) continue;
                    tidx = SubConDict[terms[j]];
                    if (!IdxToString.ContainsKey(tidx))
                        IdxToString.Add(tidx, terms[j]);
                    if (SubMatrix[hidx].ContainsKey(tidx))
                    {
                        SubMatrix[hidx][tidx]++;
                        sb = SubMatrixOccPos[hidx][tidx];
                        sb.Append(':');
                        sb.Append(termIdx.ToString());          //Append OccPos
                    }
                    else
                    {
                        SubMatrix[hidx].Add(tidx, 1);
                        SubMatrixOccPos[hidx].Add(tidx, 
                            new StringBuilder(termIdx.ToString()));
                    }
                }
            }
        }

        /// <summary>
        /// Find all terms that have have co-occurrence 
        /// with arbitary he in host entity set and with attr, 
        /// and make it as a matrix
        /// </summary>
        /// <param name="SubConDict">Sub-concepts dictionary</param>
        public void GenerateSubMatrix(List<string> sent_r, List<int> sent_r_idx, 
            string attr, Dictionary<string, int> SubConDict)
        {
            int i, len = sent_r.Count;
            TermExtractor te = new TermExtractor();
            List<string> res_te_set = new List<string>();
            StateEvent("\tGenerating SubMatrix......", false);
            for (i = 0; i < len; i++)
            {
                string str = sent_r[i];
                int cur_idx = sent_r_idx[i];
                te.ExtractTerm(str, SubConDict, ref res_te_set);
                InsertTermToSubMatrix(res_te_set.ToArray(), cur_idx, SubConDict);
                if ((i + 1) % 100 == 0)
                    ProcessEvent((int)(100.0 * (i + 1) / len));
            }
            ProcessEvent(100);
            StateEvent("\tGenerating SubMatrix complete.", true);
        }

        /// <summary>
        /// Find all terms that have co-occurrence with arbitary he in host entity set, and make it as a matrix
        /// </summary>
        /// <param name="sent_r_idx">Sorted indices for each sentences found in sent_r, we just skip them</param>
        /// <param name="SubConDict">Sub-concepts dictionary</param>
        public void GenerateTotalMatrix(List<int> sent_r_idx, Dictionary<string, int> SubConDict)
        {
            StreamReader sr;
            FileStream fsr;
            int cnt = 0, cur_idx = 0;                   //cur_idx: the current index for sent_r_idx
            int total_sent_r = sent_r_idx.Count;                    //total number of sent_r
            StateEvent("\tGenerating TotalMatrix......", false);
            #region Inherit From SubMatrix
            for (int i = 0; i < SubMatrix.Length; i++)
                TotalMatrix[i] = new Dictionary<int,int>(SubMatrix[i]);
            #endregion
            #region Read New TermDs
            fsr = new FileStream(MainFrame.TermPath, FileMode.Open);
            sr = new StreamReader(fsr, Encoding.UTF8);
            while (!sr.EndOfStream)
            {  
                string str = sr.ReadLine();
                cnt++;
                if (cur_idx < total_sent_r && cnt == sent_r_idx[cur_idx])
                    cur_idx++;                                                      //Find sentence in sent_r, skip it.
                else
                    InsertTermToMatrix(str.Split('\t'), SubConDict);      //Search this sentence
                if (cnt % 100000 == 0)
                    ProcessEvent((int)(100.0 * cnt / MainFrame.TotalSent));
            }
            ProcessEvent(100);
            sr.Close();
            fsr.Close();
            #endregion
            StateEvent("\tGenerating TotalMatrix complete.", true);
        }


        /// <summary>
        /// Initialization for generating two matrix
        /// </summary>
        /// <param name="host_entity_set">Sets containing all host entities</param>
        /// <param name="SubConDict">Sub-concept Dictionary</param>
        public void Initialize(List<string> host_entity_set, Dictionary<string, int> SubConDict)
        {
            int i, len = host_entity_set.Count;
            co_occur_h_r = new int[host_entity_set.Count];
            co_occur_r_t = new int[MainFrame.TotalSubConcept];
            IdxToString = new Dictionary<int, string>();
            MskHeToIdx = new byte[MainFrame.TotalSubConcept];
            for (i = 0; i < MskHeToIdx.Length; i++)
                MskHeToIdx[i] = 255;
            for (i = 0; i < len; i++)
                MskHeToIdx[SubConDict[host_entity_set[i]]] = Convert.ToByte(i);
            TotalMatrix = new Dictionary<int, int>[len];
            for (i = 0; i < len; i++)
                TotalMatrix[i] = new Dictionary<int, int>();
            SubMatrix = new Dictionary<int, int>[len];
            for (i = 0; i < len; i++)
                SubMatrix[i] = new Dictionary<int, int>();
            SubMatrixOccPos = new Dictionary<int,StringBuilder>[len];
            for (i = 0; i < len; i++)
                SubMatrixOccPos[i] = new Dictionary<int,StringBuilder>();
        }

        public string FindString(int idx)
        {
            if (IdxToString.ContainsKey(idx))
                return IdxToString[idx];
            return "NULL";
        }

        public int FindHR(int host_idx)
        {
            return co_occur_h_r[host_idx];
        }

        public int FindRT(string target_entity, Dictionary<string, int> SubConDict)
        {
            return co_occur_r_t[SubConDict[target_entity]];
        }

        public int FindHT(string host_entity, string target_entity, Dictionary<string, int> SubConDict)
        {
            int hidx = MskHeToIdx[SubConDict[host_entity]];
            if (hidx == 255) return -1;                                 //he not exist
            if (!SubConDict.ContainsKey(target_entity)) return -2;      //te not exist
            int tidx = SubConDict[target_entity];
            if (!TotalMatrix[hidx].ContainsKey(tidx))
                return 0;
            return TotalMatrix[hidx][tidx];
        }

        public int FindHRT(string host_entity, string target_entity, Dictionary<string, int> SubConDict)
        {
            int hidx = MskHeToIdx[SubConDict[host_entity]];
            if (hidx == 255) return -1;                                 //he not exist
            if (!SubConDict.ContainsKey(target_entity)) return -2;      //te not exist
            int tidx = SubConDict[target_entity];
            if (!SubMatrix[hidx].ContainsKey(tidx))
                return 0;
            return SubMatrix[hidx][tidx];
        }

        public string FindOccPos(string host_entity, string target_entity, Dictionary<string, int> SubConDict)
        { 
            int hidx = MskHeToIdx[SubConDict[host_entity]];
            if (hidx == 255) return "";                                 //he not exist
            if (!SubConDict.ContainsKey(target_entity)) return "";      //te not exist
            int tidx = SubConDict[target_entity];
            if (!SubMatrixOccPos[hidx].ContainsKey(tidx))
                return "";
            return SubMatrixOccPos[hidx][tidx].ToString();
        }
    }
}
